package com.sc.nft.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.sc.nft.dao.AssetChangeLogDao;
import com.sc.nft.dao.UserAssetsDao;
import com.sc.nft.dao.UserExchangeRecordDao;
import com.sc.nft.dao.UserInfoDao;
import com.sc.nft.entity.AssetChangeLog;
import com.sc.nft.entity.IntegralDetailsVO;
import com.sc.nft.entity.UserAssets;
import com.sc.nft.entity.UserInfo;
import com.sc.nft.entity.dto.BatchDeliverDTO;
import com.sc.nft.entity.dto.BatchEnterIntegrationDTO;
import com.sc.nft.entity.vo.*;
import com.sc.nft.enums.AssetChangeLogEnum;
import com.sc.nft.enums.CoinCategoryEnum;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.pool.UserPool;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import com.sc.nft.service.UserAssetsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 服务接口实现
 *
 * @author rdg
 * @since 2022-05-30 10:42:05
 * @description 由 Mybatisplus Code Generator 创建
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class UserAssetsServiceImpl implements UserAssetsService {
    private final UserAssetsDao userAssetsDao;
    private final AssetChangeLogDao assetChangeLogDao;
    private final UserExchangeRecordDao userExchangeRecordDao;
    private final UserInfoDao userInfoDao;
    //根据用户id币种枚举添加用户该币种的余额
    @Override
    public Boolean addIntegralByUserId(BigDecimal integralNum, Long userId, CoinCategoryEnum coinCategoryEnum, AssetChangeLogEnum assetChangeLogEnum,Long linkId,String linkName) {
        //变动前的余额表
        UserAssets userAssets= userAssetsDao.getByUserIdAndCoinId(userId,coinCategoryEnum.getCode());
        //添加用户积分
        UpdateWrapper<UserAssets> wrapper = new UpdateWrapper<>();
        wrapper.setSql("token_num=token_num + "+integralNum);
        wrapper.eq("user_id",userId).eq("block_chain_id",coinCategoryEnum.getCode());
        boolean update = userAssetsDao.update(wrapper);
        if (update){
            //用户积分添加成功后插入积分变动记录
            AssetChangeLog assetChangeLog=new AssetChangeLog();
            //变动前余额
            assetChangeLog.setFormNum(userAssets.getTokenNum());
            //用户id
            assetChangeLog.setUserId(userId);
            // 变动后余额
            assetChangeLog.setToNum(userAssets.getTokenNum().add(integralNum));
            //加的数量
            assetChangeLog.setTokenNum(integralNum);
            //类型code
            assetChangeLog.setType(assetChangeLogEnum.getCode());
            //类型名称
            assetChangeLog.setTypeName(assetChangeLogEnum.getName());
            assetChangeLog.setLinkId(linkId);
            assetChangeLog.setLinkName(linkName);
            assetChangeLog.setAssetId(coinCategoryEnum.getCode());
            assetChangeLog.setAssetName(coinCategoryEnum.getName());
            assetChangeLog.setIsCash(false);
            assetChangeLog.setCreateTime(new Date());
            boolean insert = assetChangeLog.insert();
        }
        return update;

    }

    //根据用户id币种枚举减少用户该币种的余额
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean subIntegralByUserId(BigDecimal integralNum, Long userId, CoinCategoryEnum coinCategoryEnum, AssetChangeLogEnum assetChangeLogEnum,Long linkId,String linkName) {
        //变动前的余额表
        UserAssets userAssets= userAssetsDao.getByUserIdAndCoinId(userId,coinCategoryEnum.getCode());
        if(NumberUtil.isLess(userAssets.getTokenNum(),integralNum)){
            throw new GlobalRunTimeException("星球碎片不足");
        }
        UpdateWrapper<UserAssets> wrapper = new UpdateWrapper<>();
        wrapper.setSql("token_num=token_num - "+integralNum);
        wrapper.setSql("version=version + "+1);
        wrapper.eq("user_id",userId).eq("block_chain_id",coinCategoryEnum.getCode());
        wrapper.eq("version",userAssets.getVersion());
        boolean update = userAssetsDao.update(new UserAssets(),wrapper);
        if (update){
            //用户积分减少成功后插入积分变动记录
            AssetChangeLog assetChangeLog=new AssetChangeLog();
            //变动前余额
            assetChangeLog.setFormNum(userAssets.getTokenNum());
            //用户id
            assetChangeLog.setUserId(userId);
            //变动后余额
            assetChangeLog.setToNum(userAssets.getTokenNum().subtract(integralNum));
            //减的数量
            assetChangeLog.setTokenNum(integralNum);
            //类型code
            assetChangeLog.setType(assetChangeLogEnum.getCode());
            //类型名称
            assetChangeLog.setTypeName(assetChangeLogEnum.getName());
            assetChangeLog.setLinkId(linkId);
            assetChangeLog.setLinkName(linkName);
            assetChangeLog.setAssetId(coinCategoryEnum.getCode());
            assetChangeLog.setAssetName(coinCategoryEnum.getName());
            assetChangeLog.setIsCash(false);
            assetChangeLog.setCreateTime(new Date());
            boolean insert = assetChangeLog.insert();
        }
        return update;
    }

    @Override
    public Page<UserAssetsVO> pageUserAssetsList(String userUuid, String nickName, String userTel, int pageNo, int pageSize) {
        Long userId = null;
        if (StrUtil.isNotBlank(userTel)){
            UserInfo userInfo = userInfoDao.getOneByUserTel(userTel);
            if (ObjectUtil.isNull(userInfo)){
                return new Page<>(pageNo,pageSize);
            }
            userId = userInfo.getId();
        }

        return userAssetsDao.pageUserAssetsList(userUuid,nickName,userId,pageNo,pageSize);
    }

    @Override
    public Page<AssetChangeLog> pageAssetsChangeList(Long userId, Integer type, Date startDate, Date endDate, int pageNo, int pageSize) {
        return assetChangeLogDao.pageAssetsChangeList( userId,type,  startDate,  endDate,  pageNo,  pageSize);
    }

    @Override
    public Page<AdminUserExchangeRecordVO> pageUserExchangeRecord(Long userId, Integer typeClassify, Date startDate, Date endDate, int pageNo, int pageSize) {
        return userExchangeRecordDao.pageUserExchangeRecord(userId,typeClassify,startDate,endDate,pageNo,pageSize);
    }

    @Override
    public Page<IntegralDetailsVO> integralDetails(Integer pageNo, Integer pageSize) {
        UserInfo user = UserPool.getUser();
        Page<IntegralDetailsVO> integralDetailsVOPage = assetChangeLogDao.integralDetails(pageNo, pageSize, user.getId());
        List<IntegralDetailsVO> records = integralDetailsVOPage.getRecords();
        for (IntegralDetailsVO record : records) {
            record.setTokenNum(new BigDecimal(record.getTokenNum()).stripTrailingZeros().toPlainString());
        }
        return integralDetailsVOPage;
    }

    @Override
    public Page<IntegralDetailsVO> integralDetailsByType(UserInfo userInfo, Integer type, Integer pageNo, Integer pageSize) {
        Page<IntegralDetailsVO> integralDetailsVOPage = assetChangeLogDao.integralDetailsByType(userInfo.getId(),type,pageNo, pageSize);
        List<IntegralDetailsVO> records = integralDetailsVOPage.getRecords();
        for (IntegralDetailsVO record : records) {
            record.setTokenNum(new BigDecimal(record.getTokenNum()).stripTrailingZeros().toPlainString());
        }
        return integralDetailsVOPage;
    }
    @Override
    @SneakyThrows
    public BatchEnterIntegrationShowVO batchEnterIntegrationShow(MultipartFile file) {
        BatchEnterIntegrationShowVO vo=new BatchEnterIntegrationShowVO();
        // 读取文档.
        ExcelReaderBuilder read = EasyExcel.read(file.getInputStream(),BatchEnterIntegrationShowListVO.class,new ReadListener<BatchEnterIntegrationShowListVO>(){
            @Override
            public void invoke(BatchEnterIntegrationShowListVO data, AnalysisContext context) {
                data.setUserTel(data.getUserTel().trim());
            }
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {

            }
        });
        // 结果集
        List<BatchEnterIntegrationShowListVO> voList = read.sheet().doReadSync();
        if (CollUtil.isEmpty(voList)) {
            throw new GlobalRunTimeException("发放数据为空");
        }
        // 前置判定处理.里面有没有手机号是个null或者""这类的存在的,如果有直接抛出异常
        vo.prepositionVerify(voList);
        // 当不为空的情况下,就开始做处理. 根据手机号关联用户账号以及用户的当前余额.在通过匹配的用户计算发放or扣减的等等积分内容计算.计算过后,组装描述内容.告知是否有异常数据
        // 把voList中的手机号转成集合
        List<String> userTelList = voList.stream().map(BatchEnterIntegrationShowListVO::getUserTel).collect(Collectors.toList());
        // 根据手机号,在用户表中查询用户信息,在进行组装.
        List<BatchEnterIntegrationShowListVO> userInfoList = userInfoDao.userTelByAssets(userTelList);
        vo.setList(vo.compare(voList,userInfoList));
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAddEnterPoints(List<BatchEnterIntegrationShowListVO> list, Boolean isAdd, String reason) {
        for (BatchEnterIntegrationShowListVO batchEnterIntegrationShowListVO : list) {
            UserInfo user = userInfoDao.getOneByUserTel(batchEnterIntegrationShowListVO.getUserTel());
            Assert.notNull(user,()->new GlobalRunTimeException(StrUtil.format("{}用户不存在",batchEnterIntegrationShowListVO.getUserTel())));
            UserAssets userAssets = userAssetsDao.getByUserIdAndCoinId(user.getId(), CoinCategoryEnum.INTEGRAL.getCode());
            Assert.notNull(userAssets,()->new GlobalRunTimeException(StrUtil.format("{}用户积分不存在",batchEnterIntegrationShowListVO.getUserTel())));
            AssetChangeLogEnum assetChangeLogEnum;
            BigDecimal toNum = BigDecimal.ZERO;

            BigDecimal controlsTokenNum = batchEnterIntegrationShowListVO.getControlsTokenNum().setScale(2, BigDecimal.ROUND_DOWN);
            if(isAdd){//加钱
                Assert.isTrue(userAssetsDao.addTokenNumByUserId(userAssets.getUserId(),controlsTokenNum),()->new GlobalRunTimeException("添加积分失败"));

                assetChangeLogEnum = AssetChangeLogEnum.ADD_BALANCE_ENTRY;
                toNum = userAssets.getTokenNum().add(controlsTokenNum);

            }else{//减钱

                Assert.isTrue(userAssetsDao.subTokenNumByUserId(userAssets.getUserId(),controlsTokenNum),()->new GlobalRunTimeException("扣减积分失败"));

                assetChangeLogEnum = AssetChangeLogEnum.SUB_BALANCE_ENTRY;
                toNum = userAssets.getTokenNum().subtract(controlsTokenNum);
            }

            //用户积分减少成功后插入积分变动记录
            AssetChangeLog assetChangeLog=new AssetChangeLog();
            //变动前余额
            assetChangeLog.setFormNum(userAssets.getTokenNum());
            //用户id
            assetChangeLog.setUserId(user.getId());
            //变动后余额
            assetChangeLog.setToNum(toNum);
            //减的数量
            assetChangeLog.setTokenNum(controlsTokenNum);
            //类型code
            assetChangeLog.setType(assetChangeLogEnum.getCode());
            //类型名称
            assetChangeLog.setTypeName(assetChangeLogEnum.getName());
            assetChangeLog.setLinkId(0L);
            assetChangeLog.setLinkName(reason);
            assetChangeLog.setAssetId(CoinCategoryEnum.INTEGRAL.getCode());
            assetChangeLog.setAssetName(CoinCategoryEnum.INTEGRAL.getName());
            assetChangeLog.setIsCash(Boolean.FALSE);
            assetChangeLog.insert();
        }

    }
}